package com.jinzhi.jzweb.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.toolkit.StringUtils;
import com.jinzhi.common.base.CoreServiceImpl;
import com.jinzhi.common.utils.Result;
import com.jinzhi.jzweb.dao.TrackDrawDao;
import com.jinzhi.jzweb.domain.StuSignUpComDO;
import com.jinzhi.jzweb.domain.TrackDrawDO;
import com.jinzhi.jzweb.service.StuSignUpComService;
import com.jinzhi.jzweb.service.TrackDrawJiangXiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 *
 * <pre>
 * 赛道抽签表（存储抽签信息）
 * </pre>
 * <small> 2023-10-16 16:36:18 | lance</small>
 */
@Service
public class TrackDrawJiangXiServiceImpl extends CoreServiceImpl<TrackDrawDao, TrackDrawDO> implements TrackDrawJiangXiService {

    @Autowired
    private StuSignUpComService stuSignUpComService;


    /**
     * 赛道抽签
     *
     * @param trackDrawDTO
     * 抽签逻辑：   抽签规则如下，须保证一所院校下三名选手、三个赛项在同一天进行考试，如湖南幼儿师范高等专科学校下有张三、李四、王五，
     * 则抽签结果应为：B赛项 1—张三、C赛项 1—李四、D赛项 1—王五。三名选手只需参加任意一项就可以了。
     *
     *          20个1
     *          11个2
     *
     * 固定规则：1、可以固定选手在哪一天（第一天、第二天）
     *         2、可以固定选手在哪个赛道（B、C、D）
     *
     * 新增需求：场次呈现不用1、2表示，用1-31表示，1-20表示第一天下午（1），21-31表示第二天上午（2）
     *
     * @return
     *
     */
    @Override
    public Result<List<TrackDrawDO>> selectDraw(TrackDrawDO trackDrawDTO) {
        //查询所有参赛选手信息
        Wrapper<StuSignUpComDO> entityWrapper =new EntityWrapper<>();
        List<StuSignUpComDO> stuSignUpComDOS = stuSignUpComService.selectList(entityWrapper);
        if(null == stuSignUpComDOS && stuSignUpComDOS.size()>0){
            return Result.fail("没有参赛选手信息，无法抽签！");
        }

        /**
         * 控制抽签的数量，第一天20个，第二天11个
         */
        final int num1 = 20;
        final int num2 = 11;

        //存储最终的抽签结果
        List<TrackDrawDO> trackDrawDOList = new ArrayList<>();

        // 打乱"stuSignUpComDOS"顺序
        Collections.shuffle(stuSignUpComDOS);
        // 对同一院校的选手分组
        Map<String, List<StuSignUpComDO>> trackDrawByUnitMap = stuSignUpComDOS.stream()
                .collect(Collectors.groupingBy(StuSignUpComDO::getUnitName));

        /**
         * 打乱“trackDrawByUnitMap”的顺序，用“shuffledMap”保存打乱后的数据 等于是在打乱B、C、D赛场的顺序
         */
        // 将键转换为列表
        List<String> keys = new ArrayList<>(trackDrawByUnitMap.keySet());
        // 对键进行排序
        Collections.shuffle(keys); // 打乱键的顺序
        // 创建一个新的有序 LinkedHashMap，按照排序后的顺序将键和值放入其中
        Map<String, List<StuSignUpComDO>> shuffledMap = new LinkedHashMap<>();
        for (String key : keys) {
            shuffledMap.put(key, trackDrawByUnitMap.get(key));
        }
        //记录每次随机第一天和第二天的次数
        int index1 = 0;
        int index2 = 0;

        /**
         * 分别记录固定的场次数量
         */
        int countPlaceNum1 = (int) shuffledMap.values().stream()
                .flatMap(List::stream)
                .filter(stuSignUpComDO -> "1".equals(stuSignUpComDO.getPlaceNum()))
                .count();

        int countPlaceNum2 = (int) shuffledMap.values().stream()
                .flatMap(List::stream)
                .filter(stuSignUpComDO -> "2".equals(stuSignUpComDO.getPlaceNum()))
                .count();
        index1+=countPlaceNum1;
        index2+=countPlaceNum2;
        Random random = new Random();
        for (Map.Entry<String, List<StuSignUpComDO>> entry : shuffledMap.entrySet()) {
            String unitName = entry.getKey();
            List<StuSignUpComDO> list = entry.getValue();
            String placeNum = "";
            /**
             * 这里还需要固定场次（第一天或者第二天）
             */
            //先判断是否存在需要固定的场次
            Optional<StuSignUpComDO> first = list.stream()
                    .filter(stuSignUpComDO -> !StringUtils.isEmpty(stuSignUpComDO.getPlaceNum()))
                    .findFirst();
            if (first.isPresent()) {
                //获取placeNum的值
                StuSignUpComDO value = first.get();
                String valuePlaceNum = value.getPlaceNum();
                if (valuePlaceNum.equals("1")){
                    placeNum = "1";
//                    index1++;
                }else {
                    placeNum = "2";
//                    index2++;
                }
            } else { //如果没有需要固定的场次，则完全随机
                //这个循环一直生成随机数，直到满足需要的条件是才会结束
                while (true) {
                    // 生成随机数
                    int randNum = random.nextInt(2);
                    // 根据随机数决定是满足 num1 还是满足 num2
                    if (randNum == 0 && index1 < num1) {
                        placeNum = "1";
                        index1++;
                        break;
                    } else if (randNum == 1 && index2 < num2) {
                        placeNum = "2";
                        index2++;
                        break;
                    }
                    // 如果 num1 和 num2 都已经满足，则跳出循环
                    if (index1 >= num1 && index2 >= num2) {
                        break;
                    }
                }
            }
            //遍历三个赛道
            TrackDrawDO trackDrawDO = new TrackDrawDO();
            trackDrawDO.setPlaceNum(placeNum);//场次
            trackDrawDO.setUnitName(unitName);//学校名称
            Set<String> processedTrackDrawValues = new HashSet<>();  // 用于记录每组 trackDraw 的值,后续方便处理不需要固定赛道的选手

            //查询是否有需要固定的选手
            boolean existsTrackDrawNotEmpty = list.stream()
                    .filter(stuSignUpComDO -> !StringUtils.isEmpty(stuSignUpComDO.getTrackDraw()))
                    .findFirst()
                    .isPresent();
            if(existsTrackDrawNotEmpty){
                for (int i = 0; i < list.size(); i++) {
                    String name = list.get(i).getName();
                    String trackDrawValue = list.get(i).getTrackDraw();
                    /**
                     * 需要固定赛道(B、C、D)的选手
                     */
                    if (!StringUtils.isEmpty(trackDrawValue)) {
                        switch (trackDrawValue) {
                            case "B":
                                trackDrawDO.setName(name);
                                break;
                            case "C":
                                trackDrawDO.setName2(name);
                                break;
                            case "D":
                                trackDrawDO.setName3(name);
                                break;
                            default:
                                break;
                        }
                        processedTrackDrawValues.add(trackDrawValue);
                    }
                }
            }
            // 处理不需要固定赛道的选手
            for (int i = 0; i < list.size(); i++) {
                String name = list.get(i).getName();
                String trackDrawValue = list.get(i).getTrackDraw();
                if (StringUtils.isEmpty(trackDrawValue)) {
                    // 处理 trackDrawValue 为空的情况
                    if (!processedTrackDrawValues.contains("B") && trackDrawDO.getName() == null) {
                        trackDrawDO.setName(name);
                    } else if (!processedTrackDrawValues.contains("C") && trackDrawDO.getName2() == null) {
                        trackDrawDO.setName2(name);
                    } else if (!processedTrackDrawValues.contains("D") && trackDrawDO.getName3() == null) {
                        trackDrawDO.setName3(name);
                    }
                }
            }
            // 添加到列表中
            trackDrawDOList.add(trackDrawDO);
        }

        /**
         * 新增需求：场次呈现不用1、2表示，用1-31表示，1-20表示第一天下午（1），21-31表示第二天上午（2）
         */
        // 使用Comparator进行排序
        Collections.sort(trackDrawDOList, new Comparator<TrackDrawDO>() {
            @Override
            public int compare(TrackDrawDO o1, TrackDrawDO o2) {
                return o1.getPlaceNum().compareTo(o2.getPlaceNum());
            }
        });
        int sort = 1; //用于记录顺序
        for (TrackDrawDO trackDrawDO : trackDrawDOList) {
            trackDrawDO.setName(sort+"-"+trackDrawDO.getName());
            trackDrawDO.setName2(sort+"-"+trackDrawDO.getName2());
            trackDrawDO.setName3(sort+"-"+trackDrawDO.getName3());
            trackDrawDO.setSort(sort++);
        }

        //同时删除之前的抽签结果和批量保存抽签的结果
        EntityWrapper<TrackDrawDO> wrapper = new EntityWrapper<>();
        this.delete(wrapper);
        if(null != trackDrawDOList && trackDrawDOList.size()>0){
            this.insertBatch(trackDrawDOList);
        }

        return Result.ok(trackDrawDOList);
    }



    /**
     * 查询上一次的抽签结果
     * @param trackDrawDTO
     * @return
     */
    @Override
    public TrackDrawDO trackList(TrackDrawDO trackDrawDTO) {

        //存储抽签你信息
        TrackDrawDO resultVO = new TrackDrawDO();
        resultVO.setHasDraw(true);//默认为true.不存在上次抽签结果时改为false

        //查询上一次的抽签结果
        Wrapper<TrackDrawDO> wrapper = new EntityWrapper<>();
        wrapper.orderBy(true,"sort");
        List<TrackDrawDO> trackDrawDOS = this.selectList(wrapper);

        //如果没有抽签上一次抽签结果那就默认初始化
        if(trackDrawDOS==null || trackDrawDOS.size()==0){
            resultVO.setHasDraw(false);
            Wrapper<StuSignUpComDO> entityWrapper =new EntityWrapper<>();
            List<StuSignUpComDO> stuSignUpComDOS = stuSignUpComService.selectList(entityWrapper);
            if(stuSignUpComDOS!=null && stuSignUpComDOS.size()>0){
                // 对同一院校的选手分组
                Map<String, List<StuSignUpComDO>> trackDrawByUnitMap = stuSignUpComDOS.stream()
                        .collect(Collectors.groupingBy(StuSignUpComDO::getUnitName));
                for (String s : trackDrawByUnitMap.keySet()) {
                    TrackDrawDO trackDrawDO = new TrackDrawDO();
                    trackDrawDO.setUnitName(s);
                    trackDrawDOS.add(trackDrawDO);
                    for (int i = 0; i < trackDrawByUnitMap.get(s).size(); i++) {
                        String name = trackDrawByUnitMap.get(s).get(i).getName();
                        if (i==0){
                            trackDrawDO.setName(name);
                        } else if (i==1) {
                            trackDrawDO.setName2(name);
                        } else if (i==2) {
                            trackDrawDO.setName3(name);
                        }else {
                            break;
                        }
                    }
                }
            }
        }
        resultVO.setTrackDrawDOList(trackDrawDOS);

        return resultVO;
    }



}
